package main

import (
	"embed"
	"errors"
	"fmt"
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/data/binding"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/driver/desktop"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"goHttp/config"
	"goHttp/internal"
	"goHttp/service"
	"io"
	"log"
	"math"
	"net/url"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	AppTitle = "同步器"
)

type UrlWidget struct {
	Url   string
	Data  binding.String
	Entry *widget.Entry
}

type TokenWidget struct {
	Data  binding.String
	Entry *widget.Entry
}

type DateTime struct {
	DateTime *widget.SelectEntry
	Data     binding.String
}
type StartName struct {
	Name *widget.SelectEntry
}
type EndName struct {
	Name *widget.SelectEntry
}

type Counter struct {
	Count chan int
	sync.RWMutex
}

type Progress struct {
	Data     binding.Float
	Progress *widget.ProgressBar
}

func NewProgress() *Progress {
	binding := binding.NewFloat()
	return &Progress{
		Data:     binding,
		Progress: widget.NewProgressBarWithData(binding),
	}
}

func (p *Progress) SetProgress(data float64) {
	p.Data.Set(data)
}

func NewDateTime(timeStr string) (dateTime *DateTime) {
	dateTime = &DateTime{}
	dateTime.Data = binding.NewString()
	dateTime.DateTime = widget.NewSelectEntry(internal.DateTime())
	dateTime.DateTime.SetText(timeStr)
	dateTime.Data.Set(timeStr)
	return
}
func (d *DateTime) Set(datetime string) {
	d.DateTime.SetText(datetime)
	d.Data.Set(datetime)
}

func NewURLWidget() []*UrlWidget {
	//地址
	UrlWidgets := make([]*UrlWidget, len(config.C.ValidateURLS()))

	for i, url := range config.C.ValidateURLS() {
		binding := binding.NewString()
		binding.Set(url.String())
		UrlWidgets[i] = &UrlWidget{
			Url:   url.String(),
			Data:  binding,
			Entry: widget.NewEntryWithData(binding),
		}
	}
	return UrlWidgets
}

func NewTokenWidget() (tokenWidget *TokenWidget) {
	tokenWidget = &TokenWidget{}
	tokenWidget.Data = binding.NewString()
	tokenWidget.Entry = widget.NewEntryWithData(tokenWidget.Data)
	return
}

func CalDays(start, end time.Time) int {
	durations := end.Sub(start)

	days := int(math.Ceil(durations.Hours() / 24))
	return days
}

func ParseTime(timeStr string, startOrEnd int) (time.Time, error) {
	var dateTime time.Time
	var err error
	if startOrEnd == 1 {
		dateTime, err = time.Parse(time.DateTime, timeStr+" 00:00:00")
	} else {
		dateTime, err = time.Parse(time.DateTime, timeStr+" 23:59:59")
	}
	return dateTime, err
}

func CalRequestTotal(requestCount int, start, end time.Time) int {
	days := CalDays(start, end)
	if days == 0 {
		return requestCount
	}
	return requestCount * days
}
func NewStartName() StartName {

	startName := StartName{
		Name: widget.NewSelectEntry(config.C.StartName),
	}
	startName.Name.SetText(config.C.StartName[0])
	return startName
}
func NewEndName() EndName {
	endName := EndName{
		Name: widget.NewSelectEntry(config.C.EndName),
	}
	endName.Name.SetText(config.C.EndName[0])
	return endName
}

//go:embed static/*
var embededFs embed.FS

func OpenSystemDir(path string) error {
	var cmd *exec.Cmd
	switch runtime.GOOS {
	case "windows":
		cmd = exec.Command("explorer", path)
	case "darwin":
		cmd = exec.Command("open", path)
	case "linux":
		// Linux 的文件管理器有很多，这里以 Nautilus 为例，你可以根据需要替换为其他文件管理器，如 dolphin、nemo 等。
		// 你可以通过读取用户的桌面环境变量来确定默认的文件管理器。
		cmd = exec.Command("nautilus", path)
		// 如果 Nautilus 不可用，可以尝试使用 xdg-open，这是一个通用的方法，可以打开默认的文件管理器。
		// cmd = exec.Command("xdg-open", path)
	default:
		return fmt.Errorf("unsupported platform: %s", runtime.GOOS)
	}

	return cmd.Run()
}

func main() {

	data, err := embededFs.ReadFile("static/complete.wav")
	if err != nil {
		log.Fatalln("嵌入文件失败：", err)
	}
	trayIconByte, err := embededFs.ReadFile("static/exe.png")
	if err != nil {
		log.Fatalln("嵌入文件失败tray icon：", err)
	}
	trayIcon := fyne.NewStaticResource(AppTitle, trayIconByte)

	logPath, errPath := service.CreateDir()

	myApp := app.New()

	if desk, ok := myApp.(desktop.App); ok {
		menus := fyne.NewMenu("菜单", fyne.NewMenuItem("查看日志", func() {
			err := OpenSystemDir(logPath)
			if err != nil {
				return
			}
		}))
		desk.SetSystemTrayMenu(menus)
		desk.SetSystemTrayIcon(trayIcon)
	}

	w := myApp.NewWindow(AppTitle)
	w.SetMaster()
	w.Resize(fyne.NewSize(700, 400))

	topTool := widget.NewToolbar(
		widget.NewToolbarAction(theme.FolderOpenIcon(), func() {
			OpenSystemDir(logPath)

		}),
		widget.NewToolbarAction(theme.ErrorIcon(), func() {
			OpenSystemDir(errPath)

		}),
		widget.NewToolbarSeparator(),
		widget.NewToolbarAction(theme.HelpIcon(), func() {
			dialog.ShowInformation("帮助", "去技术部摇人", w)
		}),
	)

	formCon := widget.NewForm()

	//时间搜索
	dateTimeStart := NewDateTime(config.C.ParseStartTime().Format("2006-01-02"))
	dateTimeStart.DateTime.OnChanged = func(s string) {
		dateTimeStart.Set(s)
	}
	dateTimeEnd := NewDateTime(config.C.ParseEndTime().Format("2006-01-02"))
	dateTimeEnd.DateTime.OnChanged = func(s string) {
		dateTimeEnd.Set(s)
	}

	tokenWdget := NewTokenWidget()
	tokenWdget.Entry.OnChanged = func(s string) {
		tokenWdget.Data.Set(s)
	}

	startName := NewStartName()
	endName := NewEndName()
	startGrid := container.NewGridWithColumns(2, startName.Name, dateTimeStart.DateTime)
	endGrid := container.NewGridWithColumns(2, endName.Name, dateTimeEnd.DateTime)
	startItem := widget.NewFormItem("开始时间", startGrid)
	endItem := widget.NewFormItem("结束时间", endGrid)
	tokenItem := widget.NewFormItem("Token", tokenWdget.Entry)
	formCon.AppendItem(startItem)
	formCon.AppendItem(endItem)
	formCon.AppendItem(tokenItem)
	formCon.AppendItem(widget.NewFormItem("", layout.NewSpacer()))
	//url

	UrlWidgets := NewURLWidget()
	for i, urlWidget := range UrlWidgets {
		formCon.AppendItem(widget.NewFormItem("地址"+strconv.Itoa(i+1), urlWidget.Entry))
	}
	//进度
	progress := NewProgress()

	formCon.SubmitText = "执行"
	logVBox := container.NewVBox()
	formCon.OnSubmit = func() {
		logVBox.RemoveAll()
		startTime := config.C.ParseStartTime()
		endTime := config.C.ParseEndTime()
		start, _ := dateTimeStart.Data.Get()
		var err error
		if len(start) >= 0 {
			startTime, err = ParseTime(start, 1)
			if err != nil {
				dialog.ShowError(errors.New("开始时间错误"), w)
				return
			}
		}

		end, _ := dateTimeEnd.Data.Get()
		if len(end) >= 0 {
			endTime, err = ParseTime(end, 2)
			if err != nil {
				dialog.ShowError(errors.New("结束时间错误"), w)
				return
			}
		}
		days := CalDays(startTime, endTime)
		if days < 0 {
			dialog.ShowError(errors.New("开始时间要小于等于结束时间"), w)
			return
		}

		token, _ := tokenWdget.Data.Get()

		urls := config.C.Urls
		temUrl := []string{}

		for i, urlWidget := range UrlWidgets {
			urlstr, err := urlWidget.Data.Get()
			if err != nil {
				dialog.ShowError(err, w)
				return
			}
			if len(urlstr) == 0 {
				continue
			}

			_, err = url.ParseRequestURI(urlstr)
			if err != nil {
				dialog.ShowError(errors.New(fmt.Sprintf("第【%d】个地址错误", i+1)), w)
				return
			}
			temUrl = append(temUrl, urlstr)
		}
		urls = temUrl

		formCon.Disable()
		total := CalRequestTotal(len(urls), startTime, endTime)
		counter := &Counter{
			Count: make(chan int, 1),
		}
		go func() {
			for {
				select {
				case val, ok := <-counter.Count:
					if ok {
						rate := float64(val) / float64(total)
						progress.SetProgress(rate)
					} else {
						formCon.Enable()
					}
				}
			}
		}()
		fmt.Println(startTime.String(), endTime.String())
		run(startTime, endTime, urls, counter, startName.Name.Text, endName.Name.Text, token, logVBox)
		service.PlayAudio(data)
		dialog.ShowInformation("完成", "执行完成", w)

	}

	formCon.Refresh()
	grid := container.NewGridWithColumns(1, formCon)
	progressGrid := container.NewGridWithColumns(1, progress.Progress)

	accrodion := widget.NewAccordion(widget.NewAccordionItem("执行日志", container.NewVScroll(logVBox)))
	accrodion.Open(0)
	body := container.NewVBox(grid, progressGrid, container.NewGridWrap(fyne.NewSize(700, 10)), container.New(layout.NewGridWrapLayout(fyne.NewSize(700, 200)), accrodion))
	main := container.NewBorder(topTool, widget.NewLabel("--by www.rrrj.net"), nil, nil, body)
	w.SetContent(main)
	w.ShowAndRun()
}

func run(startTime, endTime time.Time, urls []string, counter *Counter, startName, endName string, token string, logVBox *fyne.Container) {
	count := 0
	//检测日志目录
	logPath, errPath := service.CreateDir()

	//创建日志
	nowTime := time.Now().Format("2006-01-02-15-04-05")

	logFile, err := os.Create(logPath + "\\" + nowTime + ".log")
	if err != nil {
		panic("创建日志失败：" + err.Error())
	}
	defer logFile.Close()

	//设置日志输出到目标文件
	log.SetOutput(io.MultiWriter(logFile))

	days := CalDays(startTime, endTime)

	if days <= 0 {
		panic("起始时间设置错误")
	}

	for _, url := range urls {
		urlSlice := strings.Split(url, "/")
		for i := 0; i < days; i++ {
			newStartTime := startTime.AddDate(0, 0, i)
			newEndTime := newStartTime.Add(time.Hour * 24)
			statusCode, body := service.Do(url, newStartTime, newEndTime, startName, endName, token)
			if statusCode <= 300 && 0 <= statusCode {
				success := fmt.Sprintf("【%d】起始时间：%s~%s 【成功】 地址:%s ", count, newStartTime.Format(time.DateTime), newEndTime.Format(time.DateTime), url)
				logVBox.Add(widget.NewLabel(success))
				log.Println(success)
			} else {
				err := fmt.Sprintf("【%d】起始时间：%s~%s 【失败】 地址:%s ", count, newStartTime.Format(time.DateTime), newEndTime.Format(time.DateTime), url)
				logVBox.Add(widget.NewLabel(err))
				service.RecordErr(urlSlice, newStartTime, newEndTime, body, errPath)
			}
			count++
			counter.Lock()
			counter.Count <- count
			counter.Unlock()
		}
	}
	fmt.Println("结束...")
	defer close(counter.Count)
}
